package com.sansecy.mocklocationtest;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.GpsStatus;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Build;
import android.os.Bundle;
import android.os.SystemClock;
import android.provider.Settings;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;

import java.util.Date;

public class MainActivity extends AppCompatActivity {
    public static final int INTERVEL = 3000;
    LocationManager locationManager;
    boolean hasAddTestProvider = true;

    TextView tv_location;
    TextView tv_net_location;
    TextView tv_count;
    private int count;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv_location = findViewById(R.id.tv_location);
        tv_net_location = findViewById(R.id.tv_net_location);
        tv_count = findViewById(R.id.tv_count);

        locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);


        boolean rationale = ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION);
        Log.d(TAG, "onCreate() called with: rationale = [" + rationale + "]");
        if (!checkSelfPermission()) {
            ActivityCompat.requestPermissions(MainActivity.this, new
                    String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, 1);
        } else {
            checkMocLocationEnable(LocationManager.NETWORK_PROVIDER);
            checkMocLocationEnable(LocationManager.GPS_PROVIDER);
            new Thread(new RunnableMockLocation()).start();
            if (!hasAddTestProvider) {
                Toast.makeText(this, "请先允许模拟位置", Toast.LENGTH_SHORT).show();
            }
        }
    }

    private boolean checkSelfPermission() {
        return ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED &&
                ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        Log.d(TAG, "onActivityResult() called with: requestCode = [" + requestCode + "], resultCode = [" + resultCode + "], data = [" + data + "]");
        checkMocLocationEnable(LocationManager.NETWORK_PROVIDER);
        checkMocLocationEnable(LocationManager.GPS_PROVIDER);
        new Thread(new RunnableMockLocation()).start();
        if (!hasAddTestProvider) {
            Toast.makeText(this, "请先允许模拟位置", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * 停止模拟位置，以免启用模拟数据后无法还原使用系统位置
     * 若模拟位置未开启，则removeTestProvider将会抛出异常；
     * 若已addTestProvider后，关闭模拟位置，未removeTestProvider将导致系统GPS无数据更新；
     */
    public void stopMockLocation(String provider) {
        if (hasAddTestProvider) {
            try {
                locationManager.removeTestProvider(provider);
            } catch (Exception ex) {
                Log.e(TAG, "stopMockLocation: ", ex);
                // 若未成功addTestProvider，或者系统模拟位置已关闭则必然会出错
            }
            hasAddTestProvider = false;
        }
    }

    private static final String TAG = "MainActivity-App";

    private class RunnableMockLocation implements Runnable {

        @Override
        public void run() {
            while (true) {
                try {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            tv_count.setText(String.valueOf(++count));
                        }
                    });
                    Thread.sleep(INTERVEL);
                    mockLocation(LocationManager.GPS_PROVIDER);
                    mockLocation(LocationManager.NETWORK_PROVIDER);
                    Location location = getLastLocation(LocationManager.GPS_PROVIDER);
                    Location netProvider = getLastLocation(LocationManager.PASSIVE_PROVIDER);
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            if (netProvider != null) {
                                tv_net_location.setText(netProvider.toString());
                            }
                            if (location != null) {
                                tv_location.setText(location.toString());
                            }
                        }
                    });
                } catch (InterruptedException e) {
                    Log.e(TAG, "run: ", e);
                    e.printStackTrace();
                } catch (Exception e) {
                    Log.e(TAG, "run: ", e);
                    e.printStackTrace();
                }
            }
        }
    }

    private void mockLocation(String providerStr) {
        try {
            // 模拟位置（addTestProvider成功的前提下）
            Location mockLocation = new Location(providerStr);
            mockLocation.setLongitude(118.71650918684009);  // 经度（度）
            mockLocation.setLatitude(31.986505088792377);   // 维度（度）
            mockLocation.setAltitude(30);    // 高程（米）
            mockLocation.setBearing(180);   // 方向（度）
            mockLocation.setSpeed(0);    //速度（米/秒）
            mockLocation.setAccuracy(0.1f);   // 精度（米）
            mockLocation.setTime(new Date().getTime());   // 本地时间
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                mockLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
            }
            locationManager.setTestProviderLocation(providerStr, mockLocation);

        } catch (Exception e) {
            Log.e(TAG, "mockLocation: ", e);
            // 防止用户在软件运行过程中关闭模拟位置或选择其他应用
            stopMockLocation(providerStr);
        }
    }

    private Location getLastLocation(String provider) {
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this, new
                    String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, 1);
            return null;
        } else {
            return locationManager.getLastKnownLocation(provider);
        }
    }

    private void checkMocLocationEnable(String providerStr) {
        boolean canMockPosition = (Settings.Secure.getInt(getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION, 0) != 0)
                || Build.VERSION.SDK_INT > 22;
        if (canMockPosition) {
            try {
                LocationProvider provider = locationManager.getProvider(providerStr);
                if (provider != null) {
                    locationManager.addTestProvider(
                            provider.getName()
                            , provider.requiresNetwork()
                            , provider.requiresSatellite()
                            , provider.requiresCell()
                            , provider.hasMonetaryCost()
                            , provider.supportsAltitude()
                            , provider.supportsSpeed()
                            , provider.supportsBearing()
                            , provider.getPowerRequirement()
                            , provider.getAccuracy());
                } else {
                    locationManager.addTestProvider(
                            providerStr
                            , true, true, false, false, true, true, true
                            , Criteria.POWER_HIGH, Criteria.ACCURACY_FINE);
                }
                locationManager.setTestProviderEnabled(providerStr, true);
                Log.d(TAG, "checkMocLocationEnable() called with: providerStr = [" + providerStr + "]");
            } catch (SecurityException e) {
                hasAddTestProvider = false;
//                runOnUiThread(new Runnable() {
//                    @Override
//                    public void run() {
//                        Toast.makeText(MainActivity.this, "", Toast.LENGTH_SHORT).show();
//                    }
//                });
                Log.e(TAG, "checkMocLocationEnable: ", e);
                e.printStackTrace();
            } catch (Exception e) {
                hasAddTestProvider = false;
                Log.e(TAG, "checkMocLocationEnable: ", e);
                e.printStackTrace();
            }
        }
    }
}